package com.yfunc.ibatis3;

import java.io.InputStream;
import java.util.List;

import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import com.yfunc.ibatis3.orm.CommandInterceptor;
import com.yfunc.ibatis3.orm.EntityMapper;


public class DefaultSqlRepository implements SqlRepository {

	private SqlSessionFactory sqlSessionFactory = null;

	public DefaultSqlRepository(String configClassPath) {
		this.sqlSessionFactory = new EntitySqlSessionFactoryBuilder()
				.build(configClassPath);
	}

	public DefaultSqlRepository(Configuration configuration) {
		this.sqlSessionFactory = new EntitySqlSessionFactoryBuilder()
				.build(configuration);
	}

	public DefaultSqlRepository(SqlSessionFactory sqlSessionFactory) {
		this.sqlSessionFactory = new EntitySqlSessionFactoryBuilder()
				.build(sqlSessionFactory);
	}
	
	@Override
	public SqlSessionFactory getSqlSessionFactory() {
		return this.sqlSessionFactory;
	}
	
	@Override
	public int insert(final Object entity) {
		return doUpdate(new Handler() {
			public Object handle(EntityMapper mapper) {
				return mapper.insert(entity);
			}
		});
	}

	@Override
	public int update(final Object entity) {
		return doUpdate(new Handler() {
			public Object handle(EntityMapper mapper) {
				return mapper.update(entity);
			}
		});
	}

	@Override
	public int delete(final Object entity) {
		return doUpdate(new Handler() {
			public Object handle(EntityMapper mapper) {
				return mapper.delete(entity);
			}
		});
	}

	@Override
	public int delete(final Class<?> entityClass, final Object pk) {
		return doUpdate(new Handler() {
			public Object handle(EntityMapper mapper) {
				return mapper.deleteById(entityClass, pk);
			}
		});
	}
	
	@Override
	public <T> T load(final Class<?> entityClass, final Object pk) {
		return (T )doQuery(new Handler() {
			public Object handle(EntityMapper mapper) {
				return mapper.findById(entityClass, pk);
			}
		});
	}

	@Override
	public <T> List<T> loadAll(final Class<?> entityClass) {
		return (List<T>) doQuery(new Handler() {
			public Object handle(EntityMapper mapper) {
				return mapper.findAll(entityClass);
			}
		});
	}

	private int doUpdate(Handler handler) {
		SqlSession session = sqlSessionFactory.openSession();
		EntityMapper mapper = session.getMapper(EntityMapper.class);
		Object result = handler.handle(mapper);
		session.commit();
		return (Integer) result;
	}
	
	private Object doQuery(Handler handler) {
		SqlSession session = sqlSessionFactory.openSession();
		EntityMapper mapper = session.getMapper(EntityMapper.class);
		return handler.handle(mapper);
	}
	
	interface Handler {
		Object handle(EntityMapper mapper);
	}
	
	class EntitySqlSessionFactoryBuilder {

		public SqlSessionFactory build(String configClassPath) {
			InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream(configClassPath);
			SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
			initialConfiguration(factory.getConfiguration());
			return factory;
		}

		public SqlSessionFactory build(Configuration config) {
			initialConfiguration(config);
			return new SqlSessionFactoryBuilder().build(config);
		}
		
		public SqlSessionFactory build(SqlSessionFactory factory) {
			initialConfiguration(factory.getConfiguration());
			return factory;
		}
		
		private void initialConfiguration(Configuration config){
			config.addMapper(EntityMapper.class);
			config.addInterceptor(new CommandInterceptor());
		}
	}
	
	
	@Override
	public <T> int doUpdate(UpdateHandler<T> handler) { // do it
		SqlSession session = sqlSessionFactory.openSession();
		int count = handler.update(session);
		session.commit();
		return count;
	}

	@Override
	public <T> T doQuery(QueryHandler<T> handler) {
		SqlSession session = sqlSessionFactory.openSession();
		return handler.query(session);
	}

	
}
